/*
	Copyright (c) 2010 <copyright holders>
	This source code is licensed under The MIT License,
	see the file LICENSE for details.
	*/

#ifndef MESSAGEFILE_HPP
#define MESSAGEFILE_HPP

#include <fstream>
#include <google/protobuf/io/zero_copy_stream_impl.h>

#include "MessageRegisters.hpp"

namespace spelprog2 {
namespace file {

struct MessageFileException : public Exception {
    MessageFileException(const std::string & desc, const std::string & where) : Exception(0, "File error: " + desc, where) { }
};

class MessageInputFile {
    HIDE_EVIL_CONSTRUCTORS(MessageInputFile);

    std::string filename_;
    std::ifstream inputStream_;

    void checkForStreamFailure();
    void loadMessage(MessagePtr & msg);
public:
    MessageInputFile(const std::string & filename, const char magic[] = 0);

    //this method could be implemented more efficiently, but I don't expect it to be used a lot (ex. usage: reading a single header message)
    template <typename M>
    MessageInputFile & operator>> (M & msg)
    {
        MessagePtr m;
        *this >> m;
        if (m->GetDescriptor() != msg.GetDescriptor()) {
            throw MessageFileException("Bad message type conversion in " + filename_, WHERE);
        }
        msg.CopyFrom(*m.get());
        return *this;
    }

    void close() { inputStream_.close(); }
    bool finished() const { return inputStream_.eof() || inputStream_.fail(); }
};

template <>
inline MessageInputFile & MessageInputFile::operator>><MessagePtr> (MessagePtr & msg) { loadMessage(msg); return *this; }

class MessageOutputFile {
    HIDE_EVIL_CONSTRUCTORS(MessageOutputFile);

    std::string filename_;
    std::ofstream outputStream_;
    google::protobuf::io::OstreamOutputStream adapterStream_;
    google::protobuf::io::CodedOutputStream codedStream_;

    void checkForStreamFailure();
    void storeMessage(const Message & msg);
public:
    MessageOutputFile(const std::string & filename, bool truncate = true, const char magic[] = 0);

    MessageOutputFile & operator<< (const Message & msg)    { storeMessage(msg); return *this; }
    MessageOutputFile & operator<< (MessagePtr msg)         { storeMessage(*msg.get()); return *this; }

    void close() { outputStream_.close(); }
};

}; //file
}; //spelprog2

#endif //MESSAGEFILE_HPP